package org.yagnus.calendar;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;

/**
 * The base implementation provides the conversion between long and Date to
 * Calendar, deriving class is required to implement only one method which is
 * the happensOn(Calendar) method.
 * 
 * 
 * @author u
 * 
 */
public abstract class BaseCalendarSchedule implements CalendarDaySchedule {

	protected Locale locale = Locale.getDefault();

	public boolean happensOn(Date day) {
		return happensOn(getCalendar(day));
	}

	public boolean happensOn(long day) {
		return happensOn(getCalendar(day));
	}

	public List<Date> daysDuring(Calendar start, Calendar end) {
		Calendar itr = getCalendarDay(start);
		int endYear = end.get(Calendar.YEAR);
		int endDoy = end.get(Calendar.DAY_OF_YEAR);

		ArrayList<Date> ret = new ArrayList<Date>();
		while (itr.get(Calendar.YEAR) < endYear
				|| itr.get(Calendar.DAY_OF_YEAR) < endDoy) {

			Date itrDate = itr.getTime();

			if (happensOn(itrDate))
				ret.add(itrDate);

			itr.add(Calendar.DAY_OF_YEAR, 1);
		}
		return ret;
	}

	public List<Date> daysDuring(Date start, Date end) {
		return daysDuring(getCalendar(start), getCalendar(end));
	}

	public List<Date> daysDuring(long start, long end) {
		return daysDuring(getCalendar(start), getCalendar(end));
	}

	public Date nextTime(Calendar start) {
		Calendar itr = Calendar.getInstance(locale);
		itr.setTime(start.getTime());

		while (itr.after(start)) {

			Date itrDate = itr.getTime();

			if (happensOn(itrDate))
				return itrDate;

			itr.add(Calendar.DAY_OF_YEAR, 1);
		}
		return null;
	}

	public Date nextTime(Date start) {
		return nextTime(getCalendar(start));
	}

	public Date nextTime(long start) {
		return nextTime(getCalendar(start));
	}

	// Utility function convert to calendar
	protected Calendar getCalendar(Date d) {
		Calendar c = Calendar.getInstance(locale);
		c.setTime(d);
		return c;
	}

	protected Calendar getCalendar(long d) {
		Calendar c = Calendar.getInstance(locale);
		c.setTimeInMillis(d);
		return c;
	}

	// obtain just the date portion
	protected Calendar resetToStartOfDay(Calendar c) {

		c.set(Calendar.HOUR_OF_DAY, 0);
		c.set(Calendar.MINUTE, 0);
		c.set(Calendar.SECOND, 0);
		c.set(Calendar.MILLISECOND, 0);

		return c;
	}

	protected Calendar getCalendarDay(Calendar d) {
		Calendar c = Calendar.getInstance(locale);
		c.setTime(d.getTime());
		return resetToStartOfDay(c);
	}

	protected Calendar getCalendarDay(long d) {
		return resetToStartOfDay(getCalendar(d));
	}

	protected Calendar getCalendarDay(Date d) {
		return resetToStartOfDay(getCalendar(d));
	}

	// Check for day equality
	public static boolean sameDay(Calendar c1, Calendar c2) {
		return c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR)
				&& c1.get(Calendar.DAY_OF_YEAR) == c2.get(Calendar.DAY_OF_YEAR);
	}

	public boolean sameDay(Date d1, Date d2) {
		Calendar c1 = getCalendar(d1);
		Calendar c2 = getCalendar(d2);
		return c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR)
				&& c1.get(Calendar.DAY_OF_YEAR) == c2.get(Calendar.DAY_OF_YEAR);
	}

	public boolean sameDay(long d1, long d2) {
		Calendar c1 = getCalendar(d1);
		Calendar c2 = getCalendar(d2);
		return c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR)
				&& c1.get(Calendar.DAY_OF_YEAR) == c2.get(Calendar.DAY_OF_YEAR);
	}

}
